package com.linb.pan.server.modules.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.linb.pan.cache.core.constants.CacheConstants;
import com.linb.pan.core.constants.BPanConstants;
import com.linb.pan.core.exception.BPanBusinessException;
import com.linb.pan.core.response.ResponseCode;
import com.linb.pan.core.utils.IdUtil;
import com.linb.pan.core.utils.JwtUtil;
import com.linb.pan.core.utils.PasswordUtil;
import com.linb.pan.server.common.cache.AnnotationCacheService;
import com.linb.pan.server.modules.file.constants.FileConstants;
import com.linb.pan.server.modules.file.context.CreateFolderContext;
import com.linb.pan.server.modules.file.entity.BPanUserFile;
import com.linb.pan.server.modules.file.service.IUserFileService;
import com.linb.pan.server.modules.user.constants.UserConstants;
import com.linb.pan.server.modules.user.context.*;
import com.linb.pan.server.modules.user.converter.UserConverter;
import com.linb.pan.server.modules.user.entity.BPanUser;
import com.linb.pan.server.modules.user.mapper.BPanUserMapper;
import com.linb.pan.server.modules.user.po.CheckAnswerPO;
import com.linb.pan.server.modules.user.po.UserLoginPO;
import com.linb.pan.server.modules.user.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.linb.pan.server.modules.user.vo.UserInfoVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@Service(value = "userService")
public class UserServiceImpl extends ServiceImpl<BPanUserMapper, BPanUser> implements IUserService {

    @Autowired
    @Qualifier(value = "userAnnotationCacheService")
    private AnnotationCacheService<BPanUser> cacheService;

    @Autowired
    private UserConverter userConverter;

    @Autowired
    private IUserFileService userFileService;

    @Autowired
    private CacheManager cacheManager;

    /**
     * 用户注册
     *
     * @param userRegisterContext
     * @return
     */
    @Override
    public Long register(UserRegisterContext userRegisterContext) {
        //1.实体转换，向上下文中设置entity
        assembleUserEntity(userRegisterContext);
        //2.注册，向b_pan_user表中插入新数据，需要保证幂等性
        doRegister(userRegisterContext);
        //3.创建用户的根目录信息 操作表b_pan_user_file
        createUserRootFolder(userRegisterContext);
        //4.返回用户Id
        return userRegisterContext.getEntity().getUserId();
    }

    /**
     * 用户登录
     *
     * @param userLoginContext
     * @return
     */
    @Override
    public String login(UserLoginContext userLoginContext) {
        //1.校验用户名密码
        checkLoginInfo(userLoginContext);
        //2.生成token并保存
        generateAndSaveToken(userLoginContext);
        //3.返回token
        return userLoginContext.getAccessToken();
    }

    /**
     * 用户登出
     *
     * @param userId
     */
    @Override
    public void exit(Long userId) {
        try {
            Cache cache = cacheManager.getCache(CacheConstants.B_PAN_CACHE_NAME);
            cache.evict(UserConstants.USER_LOGIN_PREFIX + userId);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BPanBusinessException("用户登出失败");
        }
    }

    /**
     * 校验用户名
     *
     * @param usernameContext
     * @return
     */
    @Override
    public String checkUsername(CheckUsernameContext usernameContext) {
        String username = baseMapper.selectQuestionByUsername(usernameContext.getUsername());
        if (StringUtils.isBlank(username)) {
            throw new BPanBusinessException("用户名不存在");
        }
        return username;
    }

    /**
     * 校验密保答案
     *
     * @param context
     * @return
     */
    @Override
    public String checkAnswer(CheckAnswerContext context) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("username", context.getUsername());
        queryWrapper.eq("question", context.getQuestion());
        queryWrapper.eq("answer", context.getAnswer());
        int count = baseMapper.selectCount(queryWrapper);
        if (count == 0) {
            throw new BPanBusinessException("密保答案错误");
        }
        return generateAnswerToken(context);
    }

    /**
     * 重置密码
     *
     * @param context
     */
    @Override
    public void resetPassword(ResetPasswordContext context) {
        //1.校验token
        checkForgetPasswordToken(context);
        //2.重置密码
        checkAndResetPassword(context);
    }

    /**
     * 在线修改密码
     *
     * @param context
     */
    @Override
    public void changePassword(ChangePasswordContext context) {
        //1.旧密码校验
        checkOldPassword(context);
        //2.修改密码
        doChangePassword(context);
        //3.退出登录状态
        exitLoginStatus(context);
    }

    /**
     * 查询用户基本信息
     *
     * @param userId
     * @return
     */
    @Override
    public UserInfoVO getUserBaseInfo(Long userId) {
        //1.查询用户信息
        BPanUser user = getById(userId);
        if (user == null) {
            throw new BPanBusinessException("用户不存在");
        }
        //2.查询用户根目录文件信息
        BPanUserFile bPanUserFile = getUserRootFileInfo(userId);
        if (bPanUserFile == null) {
            throw new BPanBusinessException("根文件不存在");
        }
        //3.转换VO
        return userConverter.assembleUserInfoVO(user, bPanUserFile);
    }

    @Override
    public boolean removeById(Serializable id) {
        //return super.removeById(id);
        return cacheService.removeById(id);
    }

    @Override
    public boolean removeByIds(Collection<? extends Serializable> idList) {
        //return super.removeByIds(idList);
        throw new BPanBusinessException("请更换手动缓存");
    }

    @Override
    public boolean updateById(BPanUser entity) {
        return super.updateById(entity);
        //return cacheService.updateById(entity.getUserId(), entity);
    }

    @Override
    public boolean updateBatchById(Collection<BPanUser> entityList) {
        //return super.updateBatchById(entityList);
        throw new BPanBusinessException("请更换手动缓存");
    }

    @Override
    public BPanUser getById(Serializable id) {
        //return super.getById(id);
        return cacheService.getById(id);
    }

    @Override
    public List<BPanUser> listByIds(Collection<? extends Serializable> idList) {
        //return super.listByIds(idList);
        throw new BPanBusinessException("请更换手动缓存");
    }

    /*=************************************ private ************************************=*/

    /**
     * 实体转换，向上下文中设置entity
     *
     * @param userRegisterContext
     */
    private void assembleUserEntity(UserRegisterContext userRegisterContext) {
        //1.转换
        BPanUser bPanUser = userConverter.userRegisterContextToBPanUser(userRegisterContext);
        String salt = PasswordUtil.getSalt();
        //2.加密
        String dbPassword = PasswordUtil.encryptPassword(salt, userRegisterContext.getPassword());
        //3.设置缺少字段
        bPanUser.setUserId(IdUtil.get());
        bPanUser.setSalt(salt);
        bPanUser.setPassword(dbPassword);
        bPanUser.setCreateTime(new Date());
        bPanUser.setUpdateTime(new Date());
        //4.设置entity
        userRegisterContext.setEntity(bPanUser);
    }

    /**
     * 注册，向b_pan_user表中插入新数据，需要保证幂等性
     *
     * @param userRegisterContext
     */
    private void doRegister(UserRegisterContext userRegisterContext) {
        BPanUser entity = userRegisterContext.getEntity();
        if (Objects.nonNull(entity)) {
            try {
                if (!save(entity)) {
                    throw new BPanBusinessException("用户注册失败");
                }
            } catch (DuplicateKeyException duplicateKeyException) {
                //重复值，保证幂等性
                throw new BPanBusinessException("用户名已存在");
            }
            return;
        }
        throw new BPanBusinessException(ResponseCode.ERROR);
    }

    /**
     * 创建用户的根目录信息 操作表b_pan_user_file
     *
     * @param userRegisterContext
     */
    private void createUserRootFolder(UserRegisterContext userRegisterContext) {
        //1.创建对象并设置需要字段
        CreateFolderContext folderContext = new CreateFolderContext();
        folderContext.setUserId(userRegisterContext.getEntity().getUserId());
        folderContext.setParentId(FileConstants.TOP_PARENT_ID);
        folderContext.setFolderName(FileConstants.ALL_FILE_CN_STR);
        //2.调用文件模块，生成文件夹
        userFileService.createFolder(folderContext);
    }

    /**
     * 校验用户名密码
     *
     * @param userLoginContext
     */
    private void checkLoginInfo(UserLoginContext userLoginContext) {
        String username = userLoginContext.getUsername();
        String password = userLoginContext.getPassword();
        //根据用户名查询用户
        BPanUser entity = getBPanUserByUsername(username);
        if (Objects.isNull(entity)) {
            throw new BPanBusinessException("用户不存在");
        }
        String salt = entity.getSalt();
        String dbPassword = entity.getPassword();
        String encPassword = PasswordUtil.encryptPassword(salt, password);
        if (!dbPassword.equals(encPassword)) {
            throw new BPanBusinessException("密码错误");
        }
        userLoginContext.setEntity(entity);
    }

    /**
     * 生成token并保存
     *
     * @param userLoginContext
     */
    private void generateAndSaveToken(UserLoginContext userLoginContext) {
        BPanUser entity = userLoginContext.getEntity();
        String token = JwtUtil.generateToken(
                entity.getUsername(), //subject
                UserConstants.LOGIN_USER_ID, //claimKey
                entity.getUserId(), //claimValue
                UserConstants.ONE_DAY_LONG); //expire
        Cache cache = cacheManager.getCache(CacheConstants.B_PAN_CACHE_NAME);
        cache.put(UserConstants.USER_LOGIN_PREFIX + entity.getUserId(), token); //USER_LOGIN_1:token
        userLoginContext.setAccessToken(token);
    }

    /**
     * 根据用户名查询用户
     *
     * @param username
     * @return
     */
    private BPanUser getBPanUserByUsername(String username) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("username", username);
        return getOne(queryWrapper);
    }

    /**
     * 生成临时token
     *
     * @param context
     * @return
     */
    private String generateAnswerToken(CheckAnswerContext context) {
        String token = JwtUtil.generateToken(
                context.getUsername(), //subject
                UserConstants.FORGET_USERNAME,  //claimKey
                context.getUsername(),  //claimValue
                UserConstants.FIVE_MINUTES_LONG); //expire
        return token;
    }

    /**
     * 重置密码
     *
     * @param context
     */
    private void checkAndResetPassword(ResetPasswordContext context) {
        String username = context.getUsername();
        String password = context.getPassword();
        BPanUser entity = getBPanUserByUsername(username);
        if (entity == null) {
            throw new BPanBusinessException("用户不存在");
        }
        String newPassword = PasswordUtil.encryptPassword(entity.getSalt(), password);
        entity.setPassword(newPassword);
        entity.setUpdateTime(new Date());
        if (!updateById(entity)) {
            throw new BPanBusinessException("更新失败");
        }
    }

    /**
     * 校验重置密码的token
     *
     * @param context
     */
    private void checkForgetPasswordToken(ResetPasswordContext context) {
        String token = context.getToken();
        Object value = JwtUtil.analyzeToken(token, UserConstants.FORGET_USERNAME);
        if (value == null) {
            throw new BPanBusinessException(ResponseCode.TOKEN_EXPIRE);
        }
        String tokenUsername = String.valueOf(value);
        if (!context.getUsername().equals(tokenUsername)) {
            throw new BPanBusinessException("无效的token");
        }
    }

    /**
     * 旧密码校验
     *
     * @param context
     */
    private void checkOldPassword(ChangePasswordContext context) {
        Long userId = context.getUserId();
        String oldPassword = context.getOldPassword();
        BPanUser entity = getById(userId);
        if (entity == null) {
            throw new BPanBusinessException("用户不存在");
        }
        context.setEntity(entity);
        String dbPassword = entity.getPassword();
        String encPassword = PasswordUtil.encryptPassword(entity.getSalt(), oldPassword);
        if (!dbPassword.equals(encPassword)) {
            throw new BPanBusinessException("旧密码错误");
        }
    }

    /**
     * 修改密码
     *
     * @param context
     */
    private void doChangePassword(ChangePasswordContext context) {
        String newPassword = context.getNewPassword();
        BPanUser entity = context.getEntity();
        String encPassword = PasswordUtil.encryptPassword(entity.getSalt(), newPassword);
        entity.setPassword(encPassword);
        if (!updateById(entity)) {
            throw new BPanBusinessException("修改密码失败");
        }
    }

    /**
     * 退出登录状态
     *
     * @param context
     */
    private void exitLoginStatus(ChangePasswordContext context) {
        exit(context.getUserId());
    }

    /**
     * 查询用户根目录文件信息
     *
     * @param userId
     * @return
     */
    private BPanUserFile getUserRootFileInfo(Long userId) {
        return userFileService.getUserRootFile(userId);
    }

}
